/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package modelo;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 *
 * @author Diego
 */
public class EvaluacionDominio {

    DominioGeometrico dominioInicial = new DominioGeometrico();
    DominioGeometrico dominioEvaluado = new DominioGeometrico();
    Set<Punto> puntosFallidos;
    String textoLog;

    //Spaghetti
    private Spaghetti representacionSpaghetti = new Spaghetti();
    //
    private NAA representacionNAA = new NAA();

    public void setDominioInicial(DominioGeometrico vInicial)
    {
        dominioInicial = vInicial;
    }

    public void setDominioEvaluado(DominioGeometrico vEvaluado)
    {
        dominioEvaluado = vEvaluado;
    }
    
    public DominioGeometrico getDominioInicial()
    {
        return dominioInicial;
    }
    
    public DominioGeometrico getDominioEvaluado()
    {
        return dominioEvaluado;
    }

    public String getTextoLog()
    {
       return textoLog;
    }

    public void setTextoLog(String nuevoTexto)
    {
        textoLog = nuevoTexto;
    }

    public Set<Punto> getPuntosFallidos()
    {
        return puntosFallidos;
    }

    public void crearDominioEvaluado()
    {
        StringBuilder texto = new StringBuilder();
        dominioEvaluado.setConjuntoP(new HashSet<Punto>(dominioInicial.getConjuntoP()));
        dominioEvaluado.setConjuntoS(new HashSet<SegmentoLinea>(dominioInicial.getConjuntoS()));
        dominioEvaluado.setGrillaDominio(dominioInicial.getGrillaDominio());
        generarPuntosFallidos();
        if(puntosFallidos.size()==0)
            texto.append("EL DOMINIO INICIAL ES VÁLIDO, NO ES NECESARIO AJUSTARLO\n");
        else
        {
            texto.append("EL DOMINIO INICIAL ES INVALIDO\n");
            texto.append(eliminarInterseccionesEnGrilla());
            texto.append(agregarPuntosGreeneYao());
            texto.append("Inicia proceso para eliminar puntos incidentes que no son puntos finales...\n");
            texto.append(eliminarIncidentesNoFinales(generarTodosIncidentes()));
            texto.append("Termina proceso para eliminar puntos incidentes que no son puntos finales.\n");
            texto.append("Inicia proceso para eliminar intersecciónes fuera de la grilla...\n");
            texto.append(eliminarInterseccionesFueraGrilla());
            texto.append("Termina proceso para eliminar intersecciónes fuera de la grilla.\n");
        }        

        //Segmentos sin Pelos
        Set<SegmentoLinea> conjuntoSinPelos = SegmentoLinea.quitarTodosPelos(dominioEvaluado.getConjuntoS());
        texto.append(SegmentoLinea.textoConjuntoSegmentos(conjuntoSinPelos, "Conjunto Sin Pelos"));

        //Spaghetti
        Set<Punto> conjuntoPuntosSinPelos=SegmentoLinea.toConjuntoPuntos(conjuntoSinPelos);
        setRepresentacionSpaghetti(new Spaghetti());
        getRepresentacionSpaghetti().setSegmentosCerrados(conjuntoSinPelos);
        getRepresentacionSpaghetti().setConjuntoInicial(conjuntoPuntosSinPelos);
        getRepresentacionSpaghetti().enviarProcesoInicial();
        getRepresentacionSpaghetti().imprimirSpaghettis();
        getRepresentacionSpaghetti().eliminarRepetidos();
        getRepresentacionSpaghetti().imprimirSpaghettis();

        //NAA
        setRepresentacionNAA(new NAA());
        getRepresentacionNAA().setSegmentosCerrados(conjuntoSinPelos);
        getRepresentacionNAA().setConjuntoInicial(conjuntoPuntosSinPelos);
        getRepresentacionNAA().enviarProcesoInicial();
        getRepresentacionNAA().imprimirNAA();

        //Douglas-Peucker
        double parametroDouglas = 1;
        PoliLinea.imprimirListadoPolilineas(getRepresentacionNAA().getDouglasPeucker(parametroDouglas),"Douglas-Peucker");
        
        textoLog = texto.toString();
    }

    public String eliminarInterseccionesEnGrilla()
    {
        StringBuilder texto = new StringBuilder();
        texto.append("Inicia proceso para agregar puntos de interseccion no incluidos...\n");
        Iterator<SegmentoLinea> recorrido1 = dominioEvaluado.getConjuntoS().iterator();
        while(recorrido1.hasNext())
        {
            SegmentoLinea busqueda1 = recorrido1.next();
            Iterator<SegmentoLinea> recorrido2 = dominioEvaluado.getConjuntoS().iterator();
            while(recorrido2.hasNext())
            {
                SegmentoLinea busqueda2 = recorrido2.next();
                if(!busqueda1.esSimilar(busqueda2))
                {
                    Punto pInterseccion = busqueda1.interseccionSegmentoLinea(busqueda2);
                    if (pInterseccion!= null)
                    {
                        if(!busqueda1.esPuntoFinal(pInterseccion))
                        {
                            if(!busqueda2.esPuntoFinal(pInterseccion))
                            {
                                if(dominioEvaluado.getGrillaDominio().puntoEnGrilla(pInterseccion))
                                {
                                    if(!dominioEvaluado.existeEnConjuntoP(pInterseccion))
                                    {
                                        dominioEvaluado.getConjuntoP().add(pInterseccion);
                                        texto.append("Se agrega el punto: ");
                                        texto.append(pInterseccion.textoPunto()+"\n");
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        texto.append("Termina proceso para agregar puntos de interseccion no incluidos...\n");
        return texto.toString();
    }
    
    public String eliminarInterseccionesFueraGrilla()
    {
        StringBuilder texto = new StringBuilder();
        SegmentoLinea[] recorrido1 = dominioEvaluado.getConjuntoS().toArray(new SegmentoLinea[dominioEvaluado.getConjuntoS().size()]);
        for(int i=0;i<recorrido1.length;i++)
        {
            SegmentoLinea busqueda1 = recorrido1[i];
            SegmentoLinea[] recorrido2 = dominioEvaluado.getConjuntoS().toArray(new SegmentoLinea[dominioEvaluado.getConjuntoS().size()]);
            for(int j=0;j<recorrido2.length;j++)
            {
                SegmentoLinea busqueda2 = recorrido2[j];
                if(!busqueda1.esSimilar(busqueda2))
                {
                    Punto pInterseccion = busqueda1.interseccionSegmentoLinea(busqueda2);
                    if (pInterseccion!= null)
                    {
                        if(!dominioEvaluado.getGrillaDominio().puntoEnGrilla(pInterseccion))
                        {
                            Punto corte = dominioEvaluado.getGrillaDominio().puntoGrillaMasCercano(pInterseccion);
                            if(!dominioEvaluado.existeEnConjuntoP(corte))
                            {
                                dominioEvaluado.getConjuntoP().add(corte);
                                texto.append("Se agrega el punto: ");
                                texto.append(corte.textoPunto()+"\n");
                            }
                            SegmentoLinea nueva1 = new SegmentoLinea(busqueda1.getOrigen(), corte);
                            SegmentoLinea nueva2 = new SegmentoLinea(busqueda1.getFin(), corte);
                            SegmentoLinea nueva3 = new SegmentoLinea(busqueda2.getOrigen(), corte);
                            SegmentoLinea nueva4 = new SegmentoLinea(busqueda2.getFin(), corte);
                            dominioEvaluado.getConjuntoS().remove(busqueda1);
                            texto.append("Se eliminó el segmento de linea: \n");
                            texto.append(busqueda1.textoSegmentoRecta());
                            dominioEvaluado.getConjuntoS().remove(busqueda2);
                            texto.append("Se eliminó el segmento de linea: \n");
                            texto.append(busqueda2.textoSegmentoRecta());
                            if(!dominioEvaluado.existeEnConjuntoS(nueva1)&&!nueva1.esUnPunto())
                            {
                                dominioEvaluado.getConjuntoS().add(nueva1);
                                texto.append("Se agregó el segmento de linea: \n");
                                texto.append(nueva1.textoSegmentoRecta());
                            }
                            if(!dominioEvaluado.existeEnConjuntoS(nueva2)&&!nueva2.esUnPunto())
                            {
                                dominioEvaluado.getConjuntoS().add(nueva2);
                                texto.append("Se agregó el segmento de linea: \n");
                                texto.append(nueva2.textoSegmentoRecta());
                            }
                            if(!dominioEvaluado.existeEnConjuntoS(nueva3)&&!nueva3.esUnPunto())
                            {
                                dominioEvaluado.getConjuntoS().add(nueva3);
                                texto.append("Se agregó el segmento de linea: \n");
                                texto.append(nueva3.textoSegmentoRecta());
                            }
                            if(!dominioEvaluado.existeEnConjuntoS(nueva4)&&!nueva4.esUnPunto())
                            {
                                dominioEvaluado.getConjuntoS().add(nueva4);
                                texto.append("Se agregó el segmento de linea: \n");
                                texto.append(nueva4.textoSegmentoRecta());
                            }
                            texto.append(eliminarInterseccionesFueraGrilla());
                            return texto.toString();
                        }
                    }
                }
            }
        }
        return texto.toString();
    }
    
    public String eliminarIncidentesNoFinales(Set<Punto> conjuntoIndicentes)
    {
        StringBuilder texto=new StringBuilder();
        
        SegmentoLinea[] conjuntoS = dominioEvaluado.getConjuntoS().toArray(new SegmentoLinea[dominioEvaluado.getConjuntoS().size()]);
        for(int i=0;i<conjuntoS.length;i++)
        {
            SegmentoLinea verificada = conjuntoS[i];
            Set<Punto> incidentes = verificada.puntosIncidentesNoFinales(conjuntoIndicentes);
            if(incidentes != null)
            {
                if(incidentes.size()>0)
                {
                    Punto corte = incidentes.iterator().next();
                    SegmentoLinea nueva1 = new SegmentoLinea(verificada.getOrigen(), corte);
                    SegmentoLinea nueva2 = new SegmentoLinea(verificada.getFin(), corte);
                    dominioEvaluado.getConjuntoS().remove(verificada);
                    texto.append("Se eliminó el segmento de linea: \n");
                    texto.append(verificada.textoSegmentoRecta());
                    if(!dominioEvaluado.existeEnConjuntoS(nueva1))
                    {
                        dominioEvaluado.getConjuntoS().add(nueva1);
                        texto.append("Se agregó el segmento de linea: \n");
                        texto.append(nueva1.textoSegmentoRecta());
                    }
                    if(!dominioEvaluado.existeEnConjuntoS(nueva2))
                    {
                        dominioEvaluado.getConjuntoS().add(nueva2);
                        texto.append("Se agregó el segmento de linea: \n");
                        texto.append(nueva2.textoSegmentoRecta());
                    }
                    texto.append(eliminarIncidentesNoFinales(conjuntoIndicentes));
                    return texto.toString();
                }
            }
        }
        return texto.toString();
    }

    private Set<Punto> generarTodosIncidentes()
    {
        Set<Punto> todosIndicentes = new HashSet<Punto>();
        Iterator<SegmentoLinea> recorrido = dominioEvaluado.getConjuntoS().iterator();
        while(recorrido.hasNext())
        {
            SegmentoLinea verificada = recorrido.next();
            Set<Punto> incidentes = verificada.puntosIncidentesNoFinales(dominioEvaluado.getConjuntoP());
            if(incidentes != null)
            {
                if(incidentes.size()>0)
                {
                    todosIndicentes.addAll(incidentes);
                }
            }
        }
        return todosIndicentes;
    }

    private Set<Punto> generarPuntosInterseccion()
    {
        try
        {
            Set<Punto> conjuntoSalida = new HashSet<Punto>();
            Iterator<SegmentoLinea> recorrido = dominioInicial.getConjuntoS().iterator();

            while(recorrido.hasNext())
            {
                SegmentoLinea segmentoBusqueda = recorrido.next();
                conjuntoSalida.addAll(segmentoBusqueda.puntosInterseccion(dominioInicial.getConjuntoS()));
            }
            return conjuntoSalida;
        }
        catch(Exception ex)
        {
            System.err.println(ex);
            return null;
        }
    }

    private Set<Punto> generarPuntosIncidentes()
    {
        try
        {
            Set<Punto> conjuntoSalida = new HashSet<Punto>();
            Iterator<SegmentoLinea> recorrido1 = dominioInicial.getConjuntoS().iterator();
            while(recorrido1.hasNext())
            {
                SegmentoLinea segmentoObjetivo = recorrido1.next();
                conjuntoSalida.addAll(segmentoObjetivo.puntosIncidentesNoFinales(dominioInicial.getConjuntoP()));
            }
            return conjuntoSalida;
        }
        catch(Exception ex)
        {
            System.err.println(ex);
            return null;
        }
    }
    
    public void generarPuntosFallidos()
    {
        try
        {
            puntosFallidos = new HashSet<Punto>();
            Set<Punto> puntosIncidentes = generarPuntosIncidentes();
            Punto.imprimirConjuntoPuntos(puntosIncidentes, "Puntos Incidentes");
            puntosFallidos.addAll(puntosIncidentes);
            Set<Punto> puntosInterseccion = generarPuntosInterseccion();
            Punto.imprimirConjuntoPuntos(puntosInterseccion, "Puntos Interseccion");
            puntosFallidos.addAll(puntosInterseccion);
        }
        catch(Exception ex)
        {
            System.err.println(ex);
        }
    }
    
    public String agregarPuntosGreeneYao()
    {
        StringBuilder texto = new StringBuilder();
        double espacioEntrePuntosX = dominioEvaluado.getGrillaDominio().getExtensionX() / dominioEvaluado.getGrillaDominio().getColumnas();
        double espacioEntrePuntosY = dominioEvaluado.getGrillaDominio().getExtensionY() / dominioEvaluado.getGrillaDominio().getFilas();
        texto.append("Inicia proceso para agregar puntos Greene-Yao...\n");
        Iterator<SegmentoLinea> recorrido1 = dominioEvaluado.getConjuntoS().iterator();
        while(recorrido1.hasNext())
        {
            SegmentoLinea busqueda1 = recorrido1.next();
            Iterator<SegmentoLinea> recorrido2 = dominioEvaluado.getConjuntoS().iterator();
            while(recorrido2.hasNext())
            {
                SegmentoLinea busqueda2 = recorrido2.next();
                if(!busqueda1.esSimilar(busqueda2))
                {
                    Punto pInterseccion = busqueda1.interseccionSegmentoLinea(busqueda2);
                    if (pInterseccion!= null)
                    {
                        if(!busqueda1.esPuntoFinal(pInterseccion))
                        {
                            if(!busqueda2.esPuntoFinal(pInterseccion))
                            {
                                if(!dominioEvaluado.getGrillaDominio().puntoEnGrilla(pInterseccion))
                                {
                                    double saltosX = busqueda1.getSaltosEnX(espacioEntrePuntosX);
                                    double saltosY = busqueda1.getSaltosEnY(espacioEntrePuntosY);
                                    double MCD = EvaluacionDominio.maximoComunDivisor(saltosX,saltosY);
                                    if(MCD>1)
                                    {
                                        double relacionX = (saltosX/MCD)*espacioEntrePuntosX;
                                        double relacionY = (saltosY/MCD)*espacioEntrePuntosY;
                                        Punto pNuevo = pInterseccion.puntoGreeneYao(busqueda1.getOrigen(), relacionX, relacionY);
                                        if(!busqueda1.esPuntoFinal(pNuevo))
                                        {
                                            dominioEvaluado.getConjuntoP().add(pNuevo);
                                            texto.append("Se agrega el punto Greene-Yao: ");
                                            texto.append(pNuevo.textoPunto()+"\n");
                                        }
                                        pNuevo = pInterseccion.puntoGreeneYao(busqueda1.getFin(), relacionX, relacionY);
                                        if(!busqueda1.esPuntoFinal(pNuevo))
                                        {
                                            dominioEvaluado.getConjuntoP().add(pNuevo);
                                            texto.append("Se agrega el punto Greene-Yao: ");
                                            texto.append(pNuevo.textoPunto()+"\n");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        texto.append("Termina proceso para agregar puntos Greene-Yao...\n");
        return texto.toString();
    }
    
    public static double maximoComunDivisor(double v1,double v2)
    {
        // primero que todo buscamos el menor de los 2 numeros
        double menor = v1 < v2? v1:v2;
        // ahora comenzamos a dividir ambos numeros con el menor
        // y lo vamos decrementando hasta encontrar el numero
        // cuya division de como residuo cero.
        double MCD = menor; // comenzamos suponiendo que el numero menor es el MCD
        double aux = menor; // comenzamos suponiendo que el numero menor es el MCD
        while( aux >= 1 )
        {
            // la suma de los residuos de las 2 divisiones
            double residuos = (((v1 % aux) + (v2 % aux)));
            if ( residuos == 0 )
            {
                MCD = aux;
                break;
            }
            aux--;
        } 
        return MCD;
    }

    /**
     * @return the representacionSpaghetti
     */
    public Spaghetti getRepresentacionSpaghetti() {
        return representacionSpaghetti;
    }

    public NAA getRepresentacionNAA() {
        return representacionNAA;
    }

    /**
     * @param representacionSpaghetti the representacionSpaghetti to set
     */
    public void setRepresentacionSpaghetti(Spaghetti representacionSpaghetti) {
        this.representacionSpaghetti = representacionSpaghetti;
    }

    public void setRepresentacionNAA(NAA pRepresentacionNAA) {
        this.representacionNAA = pRepresentacionNAA;
    }

}
